#pragma once
#include <lcir/IR/Expression.h>
#include <lcir/IR/Lambda.h>
#include <lcir/IR/Value.h>

#include "lcir/ADT/IListIterator.h"

namespace lcir {
/**
 * @brief 该结构表示一个IRBuilder的插入点，由一个迭代去和其父lamada组成
 */
struct InsertPoint {
  Expression *expr{nullptr};
  Lambda *lambda{nullptr};
};

/**
 * @param 工厂类，用于创建表达式，并插入到lambda中
 */
class IRBuilder {
 protected:
  InsertPoint insertPoint;
  // Expression *insertPoint{nullptr};
  // Lambda *insertLambda{nullptr};

 public:
  /**
   * @param Expression insertPoint插入点，
   * 需要确保isnertPoint已经被插入在lambda内
   */
  IRBuilder(Expression *insertExpr) {
    auto l = insertExpr->getLambda();
    if (!l) throw std::runtime_error("insertPoint is not in a lambda");
    insertPoint.expr = insertExpr;
    insertPoint.lambda = l;
  }
  IRBuilder(Expression &insertExpr) : IRBuilder(&insertExpr) {}
  /**
   * @param Lambda IRbuilder将在该lambda结尾插入Expression
   */
  IRBuilder(Lambda *lambda) {
    insertPoint.lambda = lambda;
    insertPoint.expr = &(*lambda->end());
  }
  IRBuilder() = default;
  /**
   * @brief 创建表达式，将表达式插入插入点的位置
   */
  template <class OpTy, class... OpArgs>
  Expression *create(const std::vector<Value *> &operandVals, OpArgs... args) {
    auto upExpr = Expression::get<OpTy, OpArgs...>(operandVals, args...);
    return insertPoint.lambda->insertExpr(insertPoint.expr, std::move(upExpr));
  }
  template <class OpTy, class... OpArgs>
  Expression *create(OpArgs... args) {
    auto upExpr = Expression::get<OpTy, OpArgs...>(args...);
    return insertPoint.lambda->insertExpr(insertPoint.expr, std::move(upExpr));
  }

  /**
   * @brief 创建表达式，将表达式插入插入点的位置, 并返回表达式结果
   */
  template <class OpTy, class... OpArgs>
  std::vector<Value *> createAndGetRets(const std::vector<Value *> &operandVals, OpArgs... args) {
    auto *expr = create<OpTy, OpArgs...>(operandVals, args...);
    return expr->getResults();
  }
  template <class OpTy, class... OpArgs>
  Value *createAndGetRet(const std::vector<Value *> &operandVals, OpArgs... args) {
    auto *expr = create<OpTy, OpArgs...>(operandVals, args...);
    return expr->getResult();
  }

  template <class OpTy, class... OpArgs>
  Value *createAndGetRet(OpArgs... args) {
    auto *expr = create<OpTy, OpArgs...>(args...);
    return expr->getResult();
  }

  /**
   * @brief 设置插入点Expression, 作为插入点的Expression需要已经插入到lambda中
   * 注意End节点！
   */
  void setInsertPoint(Expression *insertPointExpr) {
    auto *l = insertPointExpr->getLambda();
    if (insertPointExpr->isEndNode())
      throw std::runtime_error(
          "cannot set insertPoint to End node, please use setInsertPoint(Lambda*) or setInsertPoint(INsertPoint)");
    if (!l) throw std::runtime_error("insertPoint is not in a lambda");
    insertPoint.expr = insertPointExpr;
    insertPoint.lambda = l;
  }
  void setInsertPoint(Lambda *lambda) {
    insertPoint.expr = &(*lambda->end());
    insertPoint.lambda = lambda;
  }
  void setInsertPoint(InsertPoint &ip) {
    insertPoint.expr = ip.expr;
    insertPoint.lambda = ip.lambda;
  }
  /**
   * @param 这玩意会把End节点强制转化为Expr返回，注意一下
   */
  Expression *getInsertExpr() { return insertPoint.expr; }
  Lambda *getInsertLambda() { return insertPoint.lambda; }
  InsertPoint getInsertPoint() { return insertPoint; }
};
}  // namespace lcir